package com.jiang.books.domain;

import static javax.persistence.GenerationType.SEQUENCE;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;
import javax.validation.constraints.Size;

import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import org.joda.time.DateTime;
import org.springframework.data.domain.Auditable;

/*
Create table jiang_book 
    (
		id                  Number(20,0) NOT NULL ENABLE,
		author_id           Number(20,0) NOT NULL ENABLE,
		title               Varchar2(300) NOT NULL ENABLE,
		title_photo         BLOB,
		description         Varchar2(1000),
		book_type           Varchar2(20),
		published_date      Date,
		version             Number(10,0) DEFAULT 1 NOT NULL ENABLE,
		--audit
        created_by          Varchar2(20),
        created_date        TIMESTAMP,
        last_midified_by    Varchar2(20),
        last_modified_date  TIMESTAMP,
		CONSTRAINT pk_book PRIMARY KEY (id),
		CONSTRAINT fk_book FOREIGN KEY (author_id) REFERENCES jiang_author (id) ON DELETE CASCADE ENABLE,
		--CONSTRAINT uq_title UNIQUE (title),
		CONSTRAINT ck_book_type CHECK (book_type in ('Novel', 'Story', 'Poem'))
	);
 */

@Entity
@Audited
@Table(name = "jiang_book")
@NamedQueries({
	@NamedQuery(name="Book.findAll",
			    query="select b from Book b"),
	@NamedQuery(name="Book.findAllWithAwards",
                query="select distinct b from Book b left join fetch b.awards a"),
    @NamedQuery(name="Book.findByAuthorId",
                query="select distinct b from Book b left join fetch b.author a where a.id = :id"),
    @NamedQuery(name="Book.findByAwardId",
                query="select distinct b from Book b left join fetch b.awards a where a.id = :id"),
    @NamedQuery(name="Book.findById",
			    query="select distinct b from Book b left join fetch b.awards a where b.id = :id")
})
@SqlResultSetMapping(
		name="bookResult",
		entities=@EntityResult(entityClass=Book.class)
)
public class Book implements Auditable<String, BigInteger>, Serializable {

	private static final long serialVersionUID = 1L;
	
	private BigInteger id;
    private Author author;
    private String title;
    private byte[] titlePhoto;
    private String description;
    private BookType bookType;
    private Date publishedDate;
    private int version;
    
	// Audit fields
	private String createdBy;
	private DateTime createdDate;	
	private String lastModifiedBy;
	private DateTime lastModifiedDate;

    private Set<Award> awards = new HashSet<Award>();
    private Set<User> users = new HashSet<User>();

	public Book() {
	}

	public Book(Author author, String title) {
		this.author = author;
		this.title = title;
	}

	public Book(Author author, String title, BookType bookType, Date publishedDate) {
        this.author = author;
        this.title = title;
		this.bookType = bookType;
		this.publishedDate = publishedDate;
	}

	@Id
    @GeneratedValue(strategy = SEQUENCE, generator="JIANG_SEQ")
    @SequenceGenerator(name="JIANG_SEQ", sequenceName="jiang_id_sequence", allocationSize=500)
    @Column(name = "ID", unique = true, nullable = false)
	public BigInteger getId() {
		return this.id;
	}

    public void setId(BigInteger id) {
		this.id = id;
	}

    @ManyToOne
    //@NotAudited
    @JoinColumn(name = "AUTHOR_ID", nullable = false)
    public Author getAuthor() {
        return this.author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    @Column(name = "TITLE", unique = true, nullable = false)
    @Size(min=1, max=100)
	public String getTitle() {
		return this.title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
	
    @Lob
    @Column(name = "TITLE_PHOTO")
    public byte[] getTitlePhoto() {
        return titlePhoto;
    }

    public void setTitlePhoto(byte[] titlePhoto) {
        this.titlePhoto = titlePhoto;
    }

    @Column(name = "DESCRIPTION")
    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

	@Column(name = "BOOK_TYPE")
	public BookType getBookType() {
		return this.bookType;
	}

	public void setBookType(BookType bookType) {
		this.bookType = bookType;
	}

	@Temporal(TemporalType.DATE)
    @Column(name = "PUBLISHED_DATE")
	public Date getPublishedDate() {
		return this.publishedDate;
	}

	public void setPublishedDate(Date publishedDate) {
		this.publishedDate = publishedDate;
	}

    @Version
    @Column(name = "VERSION")
    public int getVersion() {
        return this.version;
    }

    public void setVersion(int version) {
        this.version = version;
    }


    @ManyToMany
    @NotAudited
    @JoinTable(name = "jiang_author_awards",
            joinColumns = @JoinColumn(name = "BOOK_ID"),
            inverseJoinColumns = @JoinColumn(name = "AWARD_ID"))
    public Set<Award> getAwards() {
        return this.awards;
    }

    public void setAwards(Set<Award> awards) {
        this.awards = awards;
    }
    
    @ManyToMany
    @NotAudited
    @JoinTable(name = "jiang_user_books",
            joinColumns = @JoinColumn(name = "BOOK_ID"),
            inverseJoinColumns = @JoinColumn(name = "USER_ID"))
    public Set<User> getUsers() {
        return this.users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }
    
    
    // Audit methods
    @Column(name="CREATED_BY")
	public String getCreatedBy() {
		return createdBy;
	}

	public void setCreatedBy(String createdBy) {
		this.createdBy = createdBy;
	}

	@Column(name="CREATED_DATE")
	@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
	public DateTime getCreatedDate() {
		return createdDate;
	}

	public void setCreatedDate(DateTime createdDate) {
		this.createdDate = createdDate;
	}

	@Column(name="LAST_MODIFIED_BY")
	public String getLastModifiedBy() {
		return lastModifiedBy;
	}
	
	public void setLastModifiedBy(String lastModifiedBy) {
		this.lastModifiedBy = lastModifiedBy;
	}

	@Column(name="LAST_MODIFIED_DATE")
	@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
	public DateTime getLastModifiedDate() {
		return lastModifiedDate;
	}

	public void setLastModifiedDate(DateTime lastModifiedDate) {
		this.lastModifiedDate = lastModifiedDate;
	}
    
	
	@Transient
	public boolean isNew() {
		if (id == null) {
			return true;
		} else {
			return false;
		}
	}
	
	public String toString() {		
		return "Book - Id: " + getId() + ", Title: " + getTitle()  + ", Author: " + getAuthor();
	}
}